package tf2pickup;

import java.net.SocketTimeoutException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.sourceforge.rconed.SourceRcon;
import net.sourceforge.rconed.exception.BadRcon;
import net.sourceforge.rconed.exception.ResponseEmpty;

import org.pircbotx.hooks.events.MessageEvent;
import org.pircbotx.PircBotX;
import org.pircbotx.User;

/**
 * Represents an upcoming match (e.g. when the pickup is full and a game is
 * about to start)
 * It's task is to seed the teams, configure the gameserver and reset the pickup.
 * @author Philipp Preß
 */
public class Match {

    MessageEvent<PircBotX> event;
    Players players;
    HashMap<String, Integer> maps;
    ArrayList<GameServer> servers;
    Config cfg;

    /**
     * Creates a new Match with the specified arguments.
     * @param event MessageEvent from where the last player-adding happened
     * @param player the players of the pickup
     * @param maps the maps (and their votes) of the pickup
     * @param servers the gameservers of the pickup
     */
    public Match(MessageEvent<PircBotX> event, Players player, HashMap<String, Integer> maps, ArrayList<GameServer> servers) {
        this.event = event;
        this.players = player;
        this.maps = maps;
        this.servers = servers;
    }

    /**
     * This method will consecutively perform the necessary steps to get the
     * game started (seed, conf, reset)
     */
    public synchronized void start() {
        //check if the pickup is full
        if (players.getPlayersCount() < players.getMaximumSize()) {
            return;
        }

        this.seedTeams();
        this.configureGameServer();
        this.resetPickup();
    }

    /*
     * create line-up of the teams randomly and send it to the channel
     */
    private synchronized void seedTeams() {
        StringBuffer buffer = new StringBuffer();
        Random rnd = new Random();
        Players red = new Players(players.getMaximumSize() / 2);
        Players blu = new Players(players.getMaximumSize() / 2);

        //seed teams, iterate over all Tf2Classes
        for (Tf2Class tf2Class : Tf2Class.values()) {
            //iterate over all players that have choosen this Tf2Class
            for (String nickname : players.getPlayersByTf2Class(tf2Class)) {
                if (rnd.nextBoolean() && red.getPlayersByTf2Class(tf2Class).size() < tf2Class.getClassLimit()) {
                    red.addPlayer(nickname, players.getPlayerProperties(nickname));
                } else {
                    blu.addPlayer(nickname, players.getPlayerProperties(nickname));
                }
            }
        }

        //Red team
        buffer.append("Red: ");
        for (Tf2Class tf2Class : Tf2Class.values()) {
            buffer.append(tf2Class).append("(");
            for (String nickname : red.getPlayersByTf2Class(tf2Class)) {
                buffer.append(nickname).append(", ");
            }
            buffer.setLength(buffer.length() - 2);
            buffer.append(") ");
        }
        buffer.setLength(buffer.length() - 1);
        buffer.append(". ");
        event.getBot().sendMessage(event.getChannel(), buffer.toString());

        //Blu team
        buffer = new StringBuffer();
        buffer.append("Blu: ");
        for (Tf2Class tf2Class : Tf2Class.values()) {
            buffer.append(tf2Class).append("(");
            for (String nickname : blu.getPlayersByTf2Class(tf2Class)) {
                buffer.append(nickname).append(", ");
            }
            buffer.setLength(buffer.length() - 2);
            buffer.append(") ");
        }
        buffer.setLength(buffer.length() - 1);
        buffer.append(". ");
        event.getBot().sendMessage(event.getChannel(), buffer.toString());
    }

    /**
     * 
     */
    private synchronized void configureGameServer() {
        String password;
        String connectLine;
        String mostVotedMap = this.getMostVotedMap();
        GameServer server;

        server = servers.get(0);
        System.err.println(server.getHost());
        System.err.println(server.getPort());
        System.err.println(server.getRcon());
        System.err.println(mostVotedMap);

        //Send connect-info
        try {
            //Get password from server via rcon
            password = SourceRcon.send(server.getHost(), server.getPort(), server.getRcon(),
                    "sv_password");
            password = password.split("\"")[3];

            event.getBot().sendMessage(event.getChannel(), "Grab connect-info "
                    + "from pm. Have fun and good luck.");
            //Send connect-line to all players via private message
            connectLine = "connect " + server.getHost() + ":" + server.getPort()
                    + ";password " + password;
            for (User user : event.getChannel().getUsers()) {
                if (players.containsPlayer(user.getNick())) {
                    event.getBot().sendMessage(user, connectLine);
                }
            }
        } catch (SocketTimeoutException ex) {
            Logger.getLogger(Match.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadRcon ex) {
            Logger.getLogger(Match.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ResponseEmpty ex) {
            Logger.getLogger(Match.class.getName()).log(Level.SEVERE, null, ex);
        }

        //Change map
        try {
            SourceRcon.send(server.getHost(), server.getPort(), server.getRcon(),
                    "changelevel " + mostVotedMap);
        } catch (SocketTimeoutException ex) {
            Logger.getLogger(Match.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadRcon ex) {
            Logger.getLogger(Match.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ResponseEmpty ex) {
            //Do nothing, this exception is always thrown when changing the map.
        }
    }

    /**
     * 
     * @return the name of the most voted map 
     */
    private synchronized String getMostVotedMap() {
        Random random = new Random();
        ArrayList<String> mostVoted = new ArrayList<String>();
        int highestVote = 0;

        //get most voted maps into the list (probably more than one map)
        for (String map : maps.keySet()) {
            if (maps.get(map) >= highestVote) {
                if (maps.get(map) > highestVote) {
                    mostVoted.clear();
                    highestVote = maps.get(map);
                }
                mostVoted.add(map);
            }
        }
        //choose one of them randomly
        return mostVoted.get(random.nextInt(mostVoted.size()));
    }

    /**
     * resets line-up and mapvotes
     */
    private synchronized void resetPickup() {
        int maximumSize;

        //delete players
        maximumSize = players.getMaximumSize();
        players.clear();
        players = new Players(maximumSize);

        //delete votes
        for (String map : maps.keySet()) {
            maps.put(map, 0);
        }
        //Refresh Topic
        event.getBot().getListenerManager().dispatchEvent(
                new MessageEvent<PircBotX>(event.getBot(), event.getChannel(), event.getUser(), "!status"));
    }
}